Skip to content

bugfix(network): Reset network command id to keep commands in chronological order#2736

Open
Caball009 wants to merge 3 commits into
TheSuperHackers:mainfrom
Caball009:fix_network_commandID_overflow
Open

bugfix(network): Reset network command id to keep commands in chronological order#2736
Caball009 wants to merge 3 commits into
TheSuperHackers:mainfrom
Caball009:fix_network_commandID_overflow

Conversation

@Caball009
Copy link
Copy Markdown

@Caball009 Caball009 commented May 19, 2026

Synchronized network commands are given a unique id so that they can be sorted chronologically. The id needs to be unique per execution frame. It's a uint16 value so it'll overflow quite fast, especially because it's never reset and the starting value is 64000 by default.

When the network runahead decreases it's expected that multiple frames are executed right after each other. I noticed an issue with the overflow when I set the CRC interval to once per frame. When the overflow happened for a client combined with a decrease of the runahead, the order of the CRC messages was no longer guaranteed to be in chronological order for that execution frame. That would cause a mismatch if the overflow didn't happen for all clients at the same time.

With a starting value of 64000 and a CRC message every frame the first overflow happens after ~25 seconds. You can see it mismatch here because of the overflow (this was with a special test build);
https://www.youtube.com/live/V_l-q9Y-DmA?t=621s
https://www.youtube.com/live/V_l-q9Y-DmA?t=9499s

I've added some test code in the first commit. If that's used without this fix, it becomes very apparent that the messages are not in the right order when the command id overflows. On top of that, it's very likely that the disconnect bug (disconnect screen with everyone at ~59XXX msec) occurs frequently.

I intend to do a follow up PR to make the static commandID variable a part of the NetCommandMsg class.

@Caball009 Caball009 added Bug Something is not working right, typically is user facing Major Severity: Minor < Major < Critical < Blocker Network Anything related to network, servers Gen Relates to Generals ZH Relates to Zero Hour labels May 19, 2026
@Caball009 Caball009 force-pushed the fix_network_commandID_overflow branch 2 times, most recently from ee6bcd8 to 1f31061 Compare May 31, 2026 18:32
@Caball009 Caball009 marked this pull request as ready for review May 31, 2026 18:46
@Caball009 Caball009 requested a review from Mauller May 31, 2026 18:46
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 31, 2026

Greptile Summary

This PR fixes a network desync caused by the uint16 command ID counter overflowing, which caused commands for the same execution frame to be sorted out of chronological order. The fix resets the counter to 0 every MAX_FRAMES_AHEAD (128) frames during active gameplay, ensuring the ID space never wraps within a reset cycle.

  • NetworkUtil.cpp: Promotes commandID from a function-local static to a file-scope static (starting at 0 instead of 64000) and adds ResetCommandID().
  • Network.cpp: Adds a m_lastFrameResetCommandID member and calls ResetCommandID() on init() and periodically in update() when frame >= m_lastFrameResetCommandID + MAX_FRAMES_AHEAD and the game is actively advancing frames (guarded by frame + m_runAhead > m_lastExecutionFrame).
  • NetCommandList.cpp: Documents why the sort logic intentionally has no overflow handling — the retail-compatible fix is resetting the counter rather than changing the comparator.

Confidence Score: 5/5

Safe to merge. The change is a well-scoped bugfix with clear logic and no regressions introduced in the normal game flow.

The reset fires at most once per game frame and every 128 frames during active play. Within a 128-frame window the uint16 counter cannot realistically wrap (even at 100 commands/frame that is ~12,800 IDs out of 65,535). The deduplication in isEqualCommandMsg is scoped to a single frame's NetCommandList, so cross-reset ID collisions cannot occur across frame boundaries. The starting value change from 64000 to 0 is intentional and correct.

No files require special attention.

Important Files Changed

Filename Overview
Core/GameEngine/Source/GameNetwork/NetworkUtil.cpp Moves commandID from a function-local static to a file-level static and adds ResetCommandID(). First generated ID after reset is 1 (pre-increment). Starting value changed from 64000 → 0, intentional and correct with the new periodic reset.
Core/GameEngine/Include/GameNetwork/networkutil.h Adds ResetCommandID() declaration. Header already uses #pragma once. No issues.
Core/GameEngine/Source/GameNetwork/Network.cpp Adds m_lastFrameResetCommandID member, calls ResetCommandID() in init(), and adds a periodic reset every MAX_FRAMES_AHEAD (128) frames during active gameplay in update(). Also caches TheGameLogic->getFrame() into a local frame variable.
Core/GameEngine/Source/GameNetwork/NetCommandList.cpp Adds an informational comment explaining why the insert-sort does not handle commandID overflow — the retail-compatible fix is to reset the counter rather than change the sort logic.

Sequence Diagram

sequenceDiagram
    participant GL as GameLogic
    participant N as Network::update()
    participant NU as NetworkUtil (commandID)
    participant NCL as NetCommandList::addMessage()

    GL->>N: getFrame() → frame
    Note over N: Check reset condition:<br/>frame + runAhead > lastExecutionFrame<br/>AND frame ≥ lastFrameResetCommandID + MAX_FRAMES_AHEAD
    N->>NU: "ResetCommandID() → commandID = 0"
    N->>N: GetCommandsFromCommandList()
    N->>NU: "GenerateNextCommandID() → ++commandID (= 1, 2, …)"
    NU-->>N: unique ID per command
    N->>NCL: addMessage(cmd with ID)
    Note over NCL: Insert-sort by type → playerID → commandID<br/>(no overflow check needed — ID reset prevents wrap)
    N->>GL: RelayCommandsToCommandList(frame)
Loading

Reviews (2): Last reviewed commit: "Added code to reset command id." | Re-trigger Greptile

@Caball009 Caball009 force-pushed the fix_network_commandID_overflow branch from 1f31061 to ebf1fa2 Compare May 31, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Gen Relates to Generals Major Severity: Minor < Major < Critical < Blocker Network Anything related to network, servers ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant